#!/usr/bin/env python

import os
import sys
import struct
import sqlite3
import base64
from BitVector import BitVector
from optparse import OptionParser

PREFIX = '/opt/fusionstack/data/disk/bitmap/'
DBPREFIX = '/opt/fusionstack/data/chunk/'

chunktype = ['null', 'pool', 'subpool', 'vol', 'subvol', 'raw']

def read_bitmap(diskid):
    d = {}
    fname = '/opt/fusionstack/data/disk/bitmap/%d.bitmap' % diskid
    with open(fname, 'rb') as f:
        # bv = BitVector(filename='/opt/fusionstack/data/disk/bitmap/0.bitmap')
        xs = f.read()

        i = 0
        j = 0
        for x in xs:
            bv = BitVector(rawbytes=x).reverse()
            for y in bv:
                if y:
                    d[j] = 1
                    # print i, j, y
                    # i += 1
                j += 1

    # for k, v in sorted(d.items(), key=lambda x: x[0]):
    #         print k
    print '-- load bitmap diskid=%d, j=%d, len=%d' % (diskid, j, len(d))
    return d


def bmap_get(content, offset):
    byte = offset / 8
    bit = offset % 8

    binary = bin(struct.unpack('B', content[byte:byte+1])[0])[2:]
    return 1 if len(binary) >= bit + 1 and binary[-bit-1] == '1' else 0


def db_iterater(dbid):
    conn = sqlite3.connect(DBPREFIX + "%d.db"%dbid)
    conn.row_factory = sqlite3.Row
    cursor = conn.cursor()

    cursor.execute('select * from raw')
    while True:
        item = cursor.fetchone()
        if item:
            chkid = struct.unpack('QII', base64.b64decode(item['key']))
            yield {'id': "%s.%d.%d" % (chunktype[chkid[1]], chkid[0], chkid[2]), \
                    'disk':item['disk'], 'offset':item['offset']}
        else:
            break

    cursor.execute('select * from metadata')
    while True:
        item = cursor.fetchone()
        if item:
            chkid = struct.unpack('QII', base64.b64decode(item['key']))
            yield {'id': "%s.%d.%d" % (chunktype[chkid[1]], chkid[0], chkid[2]), \
                    'disk':item['disk'], 'offset':item['offset']}
        else:
            break

    cursor.close()
    conn.close()


def scan(repeat_dbg=False):
    bitmaps = {}
    checkrepeat = {}
    count = 0
    for i in range(0, 10):

        print '---db%d' % (i, )

        for item in db_iterater(i):
            diskid = item['disk']
            offset = item['offset']

            if diskid not in bitmaps:
                checkrepeat[diskid] = {}
                with open('%s/%d.bitmap' % (PREFIX, diskid), 'rb') as f:
                    bitmaps[diskid] = f.read()

            if not bmap_get(bitmaps[diskid], item['offset']):
                print "errno: %s" % (str(item))

            count += 1
            if repeat_dbg:
                if checkrepeat[diskid].has_key(item['offset']):
                    checkrepeat[diskid][offset].append(item['id'])
                else:
                    checkrepeat[diskid][offset] = []
                    checkrepeat[diskid][offset].append(item['id'])


    bitcount = 0
    for diskid, bmap in bitmaps.items():
        bv = BitVector(rawbytes=bmap)
        bitcount += bv.count_bits_sparse()

    print "sql: %d, bit: %d" % (count, bitcount, )

    for disk, offset in checkrepeat.items():
        for off, chkid in offset.items():
            if (len(chkid) > 1):
                print 'repeat: ' + str(off) + ' ' + str(chkid)

def bit(diskid, offset):
    with open('%s/%d.bitmap' % (PREFIX, diskid), 'rb') as f:
        bmap = f.read()
        bv = BitVector(rawbytes=bmap)
        b = bmap_get(bmap, offset)
        print "%d.bitmap capacity %d used %d bit %d" % (diskid, len(bmap) * 8, bv.count_bits_sparse(), b)

def main():

    parser = OptionParser()
    parser.add_option("--scan", action='store_true', default=False, dest="scan", help="scan")
    parser.add_option("--bit", action='store_true', default=False, dest="bit", help="--bit diskid offset")
    parser.add_option("--repeat", action='store_true', default=False, dest="repeat", help="--repeat checkrepeat")

    (options, args) = parser.parse_args()
    if not options.scan and not options.bit:
        sys.exit(1)

    if options.scan:
        scan(options.repeat)

    if options.bit:
        if len(sys.argv) != 4:
            print 'arg err!\n diskcheck --bit diskid offset'
            sys.exit(1)
        bit(int(sys.argv[2]), int(sys.argv[3]))

if __name__ == '__main__':
    main()
